<template>
  <m-window-frame
    :title="$t(title)"
    style="height: 100%"
    :okLoading="saveLoading"
    :ok-text="$t(okText)"
    :cancel-text="$t('关 闭')"
    class="v-201-upload"
    @cancel="handleCancel"
    @ok="handleSave"
  >
    <div class="upload-201-container">
      <el-form
        ref="ruleFormRef"
        :rules="rules"
        :model="form"
        label-width="85px"
        :hide-required-asterisk="true"
      >
        <el-form-item
          v-if="!exportZip"
          :label="$t('选择项目')"
          prop="project"
        >
          <el-row :gutter="12">
            <el-col :span="24">
              <el-select
                v-model="form.project"
                :suffix-icon="SelectSuffix"
              >
                <el-option
                  v-for="item in projectList"
                  :key="item.guid"
                  :label="item.name"
                  :value="item.guid"
                ></el-option>
              </el-select>
            </el-col>
          </el-row>
        </el-form-item>
        <el-form-item
          v-if="!exportZip"
          :label="$t('选择目录')"
          prop="dir"
        >
          <el-row :gutter="12">
            <el-col :span="24">
              <div class="g-ai-c g-flex-auto ownerBox g-one-line">
                <span class="text g-one-line">{{ form.dir.name }}</span>
                <img
                  src="statics/icons/app/iconmore.png"
                  class="_btn"
                  @click="chooseDir"
                />
              </div>
            </el-col>
          </el-row>
        </el-form-item>
        <el-form-item
          :label="$t('选择模型')"
          prop="package"
        >
          <el-row :gutter="12">
            <el-col :span="24">
              <div class="ownerBox g-one-line">
                <span
                  v-for="(item, index) in form.package"
                  :key="item.value"
                  class="ownerBox-item g-one-line"
                >
                  <img
                    v-if="item.icon"
                    :src="item.icon"
                    width="16"
                    height="16"
                    style="margin-right: 4px"
                    onerror="this.src='statics/images/sysml/Stereotype.svg'"
                    alt=""
                  />
                  <span class="text g-one-line">{{ item.label }}</span>
                  <span v-if="index < form.package.length - 1">,</span>
                </span>
                <img
                  src="statics/icons/app/iconmore.png"
                  class="_btn"
                  @click="selectPackage"
                />
              </div>
            </el-col>
          </el-row>
        </el-form-item>
        <el-form-item
          v-if="exportZip"
          :label="$t('导出目录')"
          prop="zipPath"
        >
          <el-row :gutter="12">
            <el-col
              :span="24"
              class="interface-path-box"
            >
              <el-input v-model="form.zipPath"></el-input>
              <img
                src="statics/icons/app/iconmore.png"
                class="_btn"
                @click="changePath"
              />
            </el-col>
          </el-row>
        </el-form-item>
      </el-form>
    </div>
  </m-window-frame>
</template>
<script lang="ts" setup>
import MWindowFrame from "@/common/ipcWindow/WindowFrame.vue";
import { ref, watch, reactive, onBeforeMount } from "vue";
import { Params, CloseData } from "./index";
import { useRoute } from "vue-router";
import type { FormInstance, FormRules } from "element-plus";
import { ipcWindowUtil } from "@/mdesign-lib/mdesign-app.umd.js";
import { locale } from "@/mdesign-lib/mdesign-app.umd.js";
import { ipcRenderer } from "electron";
import { IpcMsg } from "@/mdesign-lib/mdesign-main.js";
import SelectSuffix from "@/modules/model/comps/common/SelectSuffix.vue";
import { archService } from "@/mdesign-lib/mdesign-app.umd.js";
import { ChooseDirWindow } from "../chooseDir";
import { modelTreeMultiSelectWindow } from "@/mdesign-lib/mdesign-app.umd.js";
import { PropertyDataType } from "@/mdesign-lib/mdesign-app.umd.js";
import { modelService, projectService } from "@/mdesign-lib/mdesign-app.umd.js";
import { actionManager } from "@/mdesign-lib/mdesign-app.umd.js";
import { ActionKey } from "@/mdesign-lib/mdesign-app.umd.js";
import { SaveToMd3Action } from "@/modules/model/actions/project/SaveToMd3Action";
import { removeIllegalCharacters, sleep, treeForEach } from "@/util/common";
import JSZip from "jszip";
import { MetaclassType } from "@/mdesign-lib/mdesign-app.umd.js";
import ExportImageContainerVue from "@/modules/model/modals/docCreate/ExportImageContainer.vue";
import { ModelData } from "@/mdesign-lib/mdesign-app.umd.js";
import LoadingVue from "@/common/Loading.vue";
import { createReadStream, existsSync } from "fs";
import { writeFile } from "fs/promises";
import * as remote from "@electron/remote";
import * as path from "path";
import { localFile } from "@/mdesign-lib/mdesign-app.umd.js";

ipcRenderer.on(IpcMsg.WindowAbort, () => (saveLoading.value = false));

interface ProjectItem {
  guid: string;
  name: string;
  code: string;
  prjSecurityLevel: string;
}

interface DirItem {
  id: string;
  name: string;
  code: string;
  type: string;
  leaf: boolean;
}

interface OptionItem {
  label: string;
  value: string;
  icon?: string;
}

const form = reactive<Record<string, any>>({
  project: "",
  dir: {
    id: "",
    name: "",
    code: "",
    type: ""
  },
  package: [],
  zipPath: ""
});

let justValidProject = false;

const rules = reactive<FormRules>({
  project: [
    {
      required: true,
      validator: (rule, value: string, cb) => {
        if (!value) {
          return cb(new Error(locale.t("请选择项目")));
        } else {
          cb();
        }
      }
    }
  ],
  dir: [
    {
      required: true,
      validator: (rule, value: DirItem, cb) => {
        if (!form.project) {
          return cb(new Error(locale.t("请先选择项目")));
        }
        if (justValidProject) {
          return cb();
        }
        if (!value.id) {
          return cb(new Error(locale.t("请选择目录")));
        } else {
          cb();
        }
      }
    }
  ],
  package: [
    {
      required: true,
      validator: (rule, value: OptionItem[], cb) => {
        if (!value.length) {
          return cb(new Error(locale.t("请选择模型")));
        } else {
          cb();
        }
      }
    }
  ],
  zipPath: [{ required: true, trigger: "change", message: "请选择导出目录" }]
});

const saveLoading = ref(false);
const ruleFormRef = ref<FormInstance>();
let projectId = "";
let projectFilePath = "";
let projectName = "";
const route = useRoute();
const projectList = ref<ProjectItem[]>([]);
const msg = ref("");
const exportZip = ref(false);
const title = ref("");
const okText = ref("");
let loadingInstance: any = null;

watch(
  () => route.fullPath,
  () => {
    const routeQuery = route.query as unknown;
    const query = routeQuery as Params;
    projectId = query.projectId;
    projectFilePath = query.projectFilePath || "";
    projectName = query.projectName || "";
    exportZip.value = (query.exportZip as unknown as string) === "true";
    if (exportZip.value) {
      title.value = "导出zip";
      okText.value = "导 出";
    } else {
      title.value = "上传";
      okText.value = "上 传";
    }
  },
  { immediate: true }
);

onBeforeMount(() => {
  getProjectList();
});

const chooseDir = async () => {
  justValidProject = true;
  const valid = await ruleFormRef.value?.validateField(["dir"]).catch(() => false);
  justValidProject = false;
  if (!valid) return;
  const result = await ChooseDirWindow.popup({
    title: "选择目录",
    attrTip: "ChooseDir",
    methodPath: "app.archService.get201Dir",
    params: [form.project],
    lazy: false
  });
  if (result.success) {
    form.dir = result.info;
  }
};

const changePath = async () => {
  const chooseFilePathResult = await localFile.chooseSaveFilePath({
    title: "选择导出目录",
    defaultPath: projectName.split(".")[0] + ".zip",
    filters: [
      {
        name: "zip",
        extensions: ["zip"]
      }
    ]
  });
  const zipPath = chooseFilePathResult.filePath;
  if (chooseFilePathResult.canceled || !zipPath) return;
  form.zipPath = zipPath;
};

const getProjectList = async () => {
  if (!exportZip.value) {
    projectList.value = await archService.get201ProjectList();
  }
};

const selectPackage = async () => {
  const result = await modelTreeMultiSelectWindow.popup({
    loadMethod: "getPropertyTreeList",
    loadParams: JSON.stringify({
      row: {
        dataType: PropertyDataType.INSTANCE,
        moreBtnCondition: "72918954541382400",
        key: "",
        modelId: "72918954541382400",
        checkMethod: null,
        nullable: false
      },
      projectId
    }),
    rightSelectedIds: form.package.map(item => item.value),
    attrTip: "radio",
    title: "选择元素",
    delay: false,
    showNodeLabel: true,
    nullable: false,
    disableReadonlyNode: true,
    multiple: true
  });
  if (result.success) {
    const selectedId = result.selectedId as string[];
    const typeModels = await modelService.getModelDatas(projectId, selectedId);
    form.package = typeModels.map(typeModel => {
      return {
        value: typeModel.id,
        label: typeModel.name,
        icon: typeModel.icon
      };
    });
  }
};

const get201UploadFileInfo = async (projectId2: string, modelIds: string[]) => {
  const uploadInfo = await archService.get201UploadInfo(projectId2);
  msg.value = "提取元素图标...";
  const modelTreeData = uploadInfo.modelTreeData as ModelData[];
  // 提取图片
  const iconPathMap = new Map<string, string>();
  treeForEach(modelTreeData, model => {
    const icon = modelService.getModelIcon(projectId2, model.icon);
    let iconUrl = "";
    let iconPath = "";
    if (icon.startsWith("http")) {
      iconUrl = icon;
      iconPath = icon.replace(/^http:\/\/[^/]+/, "");
    } else {
      iconPath = icon;
      if (app.global.isDev) {
        iconUrl = "http://localhost:8080/" + icon;
      } else {
        iconUrl = "app://./" + icon;
      }
    }
    model.icon = iconPath;
    iconPathMap.set(iconUrl, iconPath);
  });

  const getIconDataQueue: Promise<{ buffer: Promise<ArrayBuffer>; url: string }>[] = [...iconPathMap.keys()].map(iconUrl =>
    fetch(iconUrl).then(async response => {
      if (!response.ok) {
        throw new Error(`HTTP error! Status: ${response.status}`);
      }
      return {
        buffer: response.arrayBuffer(),
        url: iconUrl
      };
    })
  );
  const modelZip = new JSZip();
  const iconDataList = await Promise.all(getIconDataQueue);
  for (let iconData of iconDataList) {
    modelZip.file(iconPathMap.get(iconData.url) as string, iconData.buffer);
  }

  // 生成压缩图片
  const folderName = "diagramImages";
  const thumbnailFilePathMap = await createThumbnail(projectId2, modelTreeData, modelZip, folderName);

  msg.value = "生成轻量化json文件...";
  modelZip.file("轻量化json文件.json", JSON.stringify(modelTreeData));

  const mainZip = new JSZip();
  mainZip.file("轻量化模型.zip", modelZip.generateAsync({ type: "arraybuffer" }));

  // 打包当前项目
  const stream = createReadStream(projectFilePath);
  mainZip.file(projectName, stream);

  // 打包resource xml
  msg.value = "生成xml文件...";
  let index = 1;
  const resourceZip = new JSZip();
  const battleGroupJson: any[] = [];
  for (let modelId of modelIds) {
    const resourceXml = await archService.export201XML(projectId2, modelId);
    const xmlName = `vms.编组-方案${index}.xml`;
    resourceZip.file(xmlName, resourceXml.data);
    index++;
    const diagramShapes = await archService.getShapeDiagrams(projectId2, modelId);
    const img: string[] = [];
    diagramShapes.forEach(diagram => {
      const diagramModelId = diagram.modelId;
      const imgPath = thumbnailFilePathMap?.get(diagramModelId);
      imgPath && img.push(imgPath);
    });
    battleGroupJson.push({ name: xmlName, img });
  }

  resourceZip.file("battleGroup.json", JSON.stringify(battleGroupJson));

  mainZip.file("resource.zip", resourceZip.generateAsync({ type: "arraybuffer" }));

  // 打包属性json
  msg.value = "生成装备数据的json...";
  const attributeJson = uploadInfo.attributeJson as string;
  mainZip.file("装备数据的json.json", JSON.stringify(attributeJson));

  // 打包清单json
  const checklist = {
    metadata: "轻量化模型.zip",
    zbsj: "装备数据的json.json",
    resource: "resource.zip",
    source: projectName
  };
  msg.value = "生成压缩包...";
  mainZip.file("json.json", JSON.stringify(checklist));
  const fileBuffer = await mainZip.generateAsync({ type: "arraybuffer" }).then(async buffer => {
    const tempDir = remote.app.getPath("temp");
    console.log("tempDir", tempDir);
    if (existsSync(tempDir)) {
      const tempZipPath = path.join(tempDir, "M-Arch.zip");
      await writeFile(tempZipPath, new Uint8Array(buffer));
    }
    // await writeFile('C:/Users/Administrator/Desktop/新建文件夹/M-Arch.zip', new Uint8Array(buffer));
    return buffer;
  });

  return fileBuffer;
};

// 生成缩略图
const createThumbnail = async (projectId2: string, modelTreeData: ModelData[], zip: JSZip, folderName: string) => {
  msg.value = "开始生成缩略图...";
  const diagramIds: string[] = [];
  const thumbnailNames = new Set<string>();
  const filePathMap = new Map<string, string>();
  treeForEach(modelTreeData, modelData => {
    if (modelData.metaclass === MetaclassType.UMLDiagram) {
      diagramIds.push(modelData.id);
      const defaultName = removeIllegalCharacters(modelData.name);
      const thumbnailName = getThumbnailName(defaultName, thumbnailNames);
      thumbnailNames.add(thumbnailName);
      const filePath = folderName + `/${thumbnailName}.jpg`;
      filePathMap.set(modelData.id, filePath);
      (modelData as any).imagePath = filePath;
    }
    delete (modelData as any).metaclass;
  });
  const ratioScale = 2560 / (window.screen.width * devicePixelRatio);
  if (diagramIds.length === 0) return;
  const allCount = diagramIds.length;
  let finshedCount = 0;
  await ExportImageContainerVue.popup({
    path: "",
    projectId: projectId2,
    modelIds: diagramIds,
    ratioScale,
    isPng: false,
    fileFullPathMap: filePathMap,
    zip,
    updateExportedNum: () => {
      finshedCount++;
      msg.value = `正在生成缩略图(${finshedCount}/${allCount})...`;
    }
  }).catch(e => console.error(e));
  return filePathMap;
};

const getThumbnailName = (defaultName: string, thumbnailNames: Set<string>) => {
  let thumbnailName = defaultName;
  let index = 1;
  while (thumbnailNames.has(thumbnailName)) {
    thumbnailName = defaultName + `(${index})`;
    index++;
  }
  return thumbnailName;
};

const handleSave = async () => {
  if (!ruleFormRef.value) return;
  const valid = await ruleFormRef.value.validate().catch(() => false);
  if (!valid) return;
  saveLoading.value = true;
  const selectedProject = projectList.value.find(item => item.guid === form.project);

  const info = {
    prjGuid: selectedProject?.guid || "",
    srcTool: "march",
    securityLevel: selectedProject?.prjSecurityLevel || "",
    catalogId: form.dir.id,
    modelIds: form.package.map(item => item.value)
  };
  try {
    await projectService.clearStep(projectId);
    await (actionManager.getAction(ActionKey.saveToMd3) as SaveToMd3Action).run(projectId);
    msg.value = "获取项目数据...";
    LoadingVue.popup({
      msg,
      getPortal: (item: any) => {
        loadingInstance = item;
      }
    });
    await sleep(1);
    const fileInfo = await get201UploadFileInfo(projectId, info.modelIds);
    if (exportZip.value) {
      msg.value = "导出文件中...";
      await writeFile(form.zipPath, new Uint8Array(fileInfo));
      loadingInstance?.removeImmediately();
      ipcWindowUtil.sendWindowChange({ success: true });
      return;
    }
    msg.value = "上传文件中...";
    const fileData = new FormData();
    const file = new Blob([new Uint8Array(fileInfo)]);
    fileData.append("prjGuid", info.prjGuid);
    fileData.append("srcTool", info.srcTool);
    fileData.append("securityLevel", info.securityLevel);
    fileData.append("catalogId", info.catalogId);
    fileData.append("file", file, "M-Arch.zip");
    const result = await archService.upload201Project(fileData);
    loadingInstance?.removeImmediately();
    if (result.status === "0") {
      ipcWindowUtil.sendWindowChange({ success: true });
    } else {
      ipcWindowUtil.sendWindowChange({ success: false, info: result.msg?.toString() || "" });
    }
  } catch (error: any) {
    loadingInstance?.removeImmediately();
    ipcWindowUtil.sendWindowChange({ success: false, info: error?.toString() || "" });
  } finally {
    saveLoading.value = false;
  }
};

function handleclose(data: CloseData) {
  saveLoading.value = false;
  ipcWindowUtil.closeWindow(data);
}
function handleCancel() {
  handleclose({ success: false });
}
</script>
<style lang="scss">
body .el-select-dropdown .el-select-dropdown__list {
  max-height: 150px;
}
.v-201-upload {
  flex: 1;
  display: flex;
  overflow: hidden;
  .upload-201-container {
    flex: 1;
    overflow: hidden;
    padding: 32px;
    .el-form-item {
      height: 32px;
      margin-bottom: 14px;
      .el-form-item__label {
        line-height: 32px;
        text-overflow: ellipsis;
        overflow: hidden;
        white-space: nowrap;
      }
      .el-input__wrapper {
        border: 1px solid rgba(0, 0, 0, 0.15);
        box-shadow: none;
        input {
          height: 24px;
        }
      }
      .el-select {
        width: 100%;
        .el-select__wrapper {
          border: 1px solid rgba(0, 0, 0, 0.15);
          box-shadow: none;
          border-radius: 0;
          height: 28px;
          .el-select__icon {
            transition: none;
          }
        }
        .el-select__icon {
          width: 20px;
        }
      }
      .el-button {
        border-color: rgba(0, 0, 0, 0.15);
        padding: 8px;
        border-radius: 0;
        &:hover {
          border-color: var(--md-primary-color);
        }
      }
      .ownerBox {
        width: 100%;
        line-height: 28px;
        height: 28px;
        border: 1px solid rgba(0, 0, 0, 0.15);
        position: relative;
        padding-left: 7px;
        padding-right: 34px;
        vertical-align: middle;
        .ownerBox-item {
          line-height: 28px;
          img {
            font-size: 0;
            line-height: 16px;
          }
          span {
            font-size: 12px;
            line-height: 14px;
          }
        }
        ._btn {
          position: absolute;
          display: inline-block;
          cursor: pointer;
          top: 50%;
          right: 7px;
          transform: translateY(-50%);
        }
      }
    }

    .el-row {
      width: 100%;
      .el-col {
        min-height: 32px;
        display: flex;
        align-items: center;
      }
    }
    .interface-path-box {
      position: relative;
      .el-input__wrapper {
        padding-right: 34px;
      }
      ._btn {
        position: absolute;
        display: inline-block;
        cursor: pointer;
        top: 50%;
        right: 14px;
        transform: translateY(-50%);
      }
    }
  }
}
</style>
